package search.genes;

import static gpinterpreter.vector.VecSymbol.MOVE;
import static gpinterpreter.vector.VecSymbol.NOP;
import gpinterpreter.vector.VecInstruction;
import gpinterpreter.vector.VecSymbol;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.InvalidConfigurationException;
import org.jgap.impl.FixedBinaryGene;

public class BitFieldInstructionGene extends FixedBinaryGene implements
		InstructionGene {

	//TODO: implement newGene method so these actually work as expected.
	protected Gene newGeneInternal() {
		try {
			BitFieldInstructionGene result = new BitFieldInstructionGene(getConfiguration());
			result.setGraphSize(graphSize);
			return result;
		} catch (InvalidConfigurationException iex) {
			throw new IllegalStateException(iex.getMessage());
		}
	}
	public int graphSize;
	private static VecSymbol[] symbols = {MOVE, /* SPLIT, */ NOP // ,MERGE
	};

	public BitFieldInstructionGene(Configuration a_config, int graphSize) throws InvalidConfigurationException{
		this(a_config);
		this.graphSize = graphSize;
	}
	
	public BitFieldInstructionGene(BitFieldInstructionGene toCopy) throws InvalidConfigurationException {
		super(toCopy.getConfiguration(), toCopy);
		graphSize = toCopy.graphSize;
	}

	public InstructionGene clone() {
		try {
			return new BitFieldInstructionGene(this);
		} catch (InvalidConfigurationException ex) {
			Logger.getLogger(BitFieldInstructionGene.class.getName()).log(Level.SEVERE, null, ex);
			throw new RuntimeException("Attempt to clone BitFieldInstructionGene failed.");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see searcher.genes.InstructionGene#setGraphSize(int)
	 */
	@Override
	public void setGraphSize(int s) {
		graphSize = s;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see searcher.genes.InstructionGene#getGraphSize()
	 */
	@Override
	public int getGraphSize() {
		return graphSize;
	}

	public BitFieldInstructionGene(Configuration a_config)
			throws InvalidConfigurationException {
		super(a_config, 32);

	}
	private static final long serialVersionUID = 1L;

	public int bitAsInt(int index) {
		return (getBit(index)) ? 1 : 0;
	}

	private static VecSymbol getSymbol(int val) {

		int instruction = val >>> 30;

		switch (instruction) {
			case 0:
			case 1:
				return MOVE;
			// break;
			case 2:
			case 3:
			default:
				return NOP;
			// break;
		}

	}

	/**
	 * Decode the Integer representation to an executable VecInstruction.
	 */
	public static VecInstruction getInstruction(int val, int graphSize) {

		VecSymbol operator = getSymbol(val);

		int lower = val & 0x7FFF;
		int higher = (val >>> 15) & 0x7FFF;

		lower = (lower % (graphSize)) + 1;
		higher = (higher % (graphSize)) + 1;

		return new VecInstruction(operator, lower, higher, -1);

	}

	private static int bin2int(boolean v) {
		return v ? 1 : 0;
	}

	public static int asInteger(FixedBinaryGene fbg) {
		int val = 0;
		for (int i = 0; i < 32; i++) {
			val += bin2int(fbg.getBit(i)) << i;
		}

		return val;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see searcher.genes.InstructionGene#getInstruction()
	 */
	@Override
	public VecInstruction getInstruction() {

		int val = 0;
		for (int i = 0; i < 32; i++) {
			val += bitAsInt(i) << i;
		}

		return getInstruction(val, graphSize);
	}

	public String toString() {
		return getInstruction().toString();
	}

	
}